//============================================================================
// Copyright (C) 2003-2013 Brett R. Jones 
// All Rights Reserved
//
// You may redistribute and/or modify for non commercial and commercial uses 
// provided this copyright notice remains in place and is not modified
//
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================

#include "VxPktLib.h"
#include <VxUtilLib/VxFileUtil.h>
#include <VxUtilLib/VxFileCrc32.h>
#include <VxUtilLib/VxBaseSktInfo.h>
#include <stdio.h>
#include <stdarg.h>

//---------------------------------------------------------------------------
//=== file tx/rx functions ===//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//! override this function to be notified when a file is recieved
long VxPktHandler::OnFileRecieved( VxSktBase * poSkt, void * pvUserData, const char * pFileName )
{
	return 0;
}
//---------------------------------------------------------------------------
//! override this function to be notified when a file is sent
long VxPktHandler::OnFileSent( VxSktBase * poSkt, void * pvUserData, const char * pFileName )
{
	return 0;
}

//---------------------------------------------------------------------------
//=== helper file tx/rx methods ===//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//! que a file to be sent
long VxPktHandler::QueFileToSend(	VxSktBase * poSkt, 
										void * pvUserData, 
										const char * pLclFileName, 
										const char * pRmtFileName, 
										BOOL bBeginSendingFiles )
{
	VxBaseSktInfo * poSktInfo = ( VxBaseSktInfo * )pvUserData;
	poSktInfo->QueFileToSend( pLclFileName, pRmtFileName );
	if( bBeginSendingFiles )
	{
		return BeginFileSend( poSkt, pvUserData );
	}
	return 0;
}
//---------------------------------------------------------------------------
//! begin sending qued files
long VxPktHandler::BeginFileSend( VxSktBase * poSkt, void * pvUserData )
{
	PktCmd1ArgReq oPktReq;
	VxBaseSktInfo * poSktInfo = ( VxBaseSktInfo * )pvUserData;
	if( poSktInfo->m_astrFilesToSend.size() < 2 )
	{
		log_msg( 0, "VxPktHandler::BeginFileSend: No files to send" );
		return -1;
	}

	VxFileXferInfo * poXferInfo =	&poSktInfo->m_oTxFileInfo;
	if( poXferInfo->m_hFile )
	{
		log_msg( 0, "VxPktHandler::BeginFileSend: ERROR: File transfer still in progress" );
		return -1;
	}
	/*
	// set the source and destination file names
	strcpy( poXferInfo->m_strLocalFileName.c_str(), poSktInfo->m_astrFilesToSend[0].c_str() );
	strcpy( poXferInfo->m_as8RemoteFileName, poSktInfo->m_astrFilesToSend[1].c_str() );
	// make full path to the local file
	VxMakeFullPath( poXferInfo->m_strLocalFileName.c_str() );

	poXferInfo->m_u64FileOffs = 0;

	U64 u64FileLen = VxGetFileLen( poXferInfo->m_strLocalFileName.c_str() ); // file length
	poXferInfo->m_u64FileLen = u64FileLen; // save file len
	if( 0 == u64FileLen )
	{
		// no file found to send
		log_msg( 0, "VxPktHandler ERROR: File %s not found to send", poXferInfo->m_strLocalFileName.c_str() );
		return -1;
	}
	poXferInfo->m_hFile = fopen( poXferInfo->m_strLocalFileName.c_str(), "rb" ); 
	if( NULL == poXferInfo->m_hFile )
	{
		// open file failed
		poXferInfo->m_hFile = NULL;
		log_msg( 0, "SlytronPktHandlerr ERROR: Could not open File %s", poXferInfo->m_strLocalFileName.c_str() );
		return -1;
	}
	// send ack with file len
	oPktReq.u16Cmd = CMD_TYPE_FILE_SEND;
	oPktReq.u64Arg2 = CMD_OPTION_REPLACE_IF_FILE_EXIST;
	oPktReq.u64Arg3 = poXferInfo->m_u64FileLen;
	strcpy( oPktReq.as8Arg1, poXferInfo->m_as8RemoteFileName );
	VxSendPkt( poSkt, &oPktReq );
	log_msg( 0, "VxPktHandler:(File Send) start sending file %s from Admin %s", 
					(const char *)poXferInfo->m_strLocalFileName.c_str(),
					(const char *)poSktInfo->m_csUserName.c_str() );
*/
	// file is open and setup so send first chunk of data
	return TxNextFileChunk( poSkt, poSktInfo, NULL );
}

//============================================================================
//=== helper methods ===//
//============================================================================
//============================================================================
//! send next part of file.. assumes user data dirived from VxSktInfoBase
long VxPktHandler::TxNextFileChunk( VxSktBase * poSkt, VxBaseSktInfo * poSktInfo, PktFileChunkAck * poPkt )
{
	vx_assert( poSktInfo );
	// fill the packet with data from the file
	RCODE rc = 0;
	VxFileXferInfo * poXferInfo = &poSktInfo->m_oTxFileInfo;
	vx_assert( poXferInfo->m_hFile );
	vx_assert( poXferInfo->m_u64FileLen );
	if( poXferInfo->m_u64FileOffs >= poXferInfo->m_u64FileLen )
	{
		//we are done sending file
		if( poXferInfo->m_hFile )
		{
			fclose( poXferInfo->m_hFile );
			poXferInfo->m_hFile  = NULL;
		}
		// move file from to be sent to sent
		poSktInfo->m_astrFilesSent.push_back( poSktInfo->m_astrFilesToSend[0] );
		poSktInfo->m_astrFilesToSend.erase( poSktInfo->m_astrFilesToSend.begin() );
		poSktInfo->m_astrFilesToSend.erase( poSktInfo->m_astrFilesToSend.begin() );

		PktFileSendComplete oPkt;
		VxCalculateFileCheckSum( poXferInfo->m_strLocalFileName.c_str(), oPkt.u32CheckSum );
		VxSendPkt( poSkt, &oPkt );
		PktErrorMsg( "Slytron Client:: %s Done Sending file %s", (const char *)poXferInfo->m_strUserName.c_str(), poXferInfo->m_strLocalFileName.c_str() );
		OnFileSent( poSkt, poSktInfo, poXferInfo->m_strLocalFileName.c_str() );
		// send next file if available
		return BeginFileSend( poSkt, poSktInfo );
	}

	PktFileChunk oPkt;
	// see how much we can read
	U32 u32ChunkLen = (U32)(poXferInfo->m_u64FileLen - poXferInfo->m_u64FileOffs);
	if( MAX_FILE_CHUNK_LEN < u32ChunkLen )
	{
		u32ChunkLen = MAX_FILE_CHUNK_LEN;
	}
	// read data into packet
	U32 u32BytesRead = (U32)fread(	oPkt.au8FileChunk,
									1,
									u32ChunkLen,
									poXferInfo->m_hFile );
	if( u32BytesRead != u32ChunkLen )
	{
		// error reading file
		rc = VxGetLastError();
		if( 0 == rc )
		{
			// no error givin so make it up
			rc = -1;
		}
		// close the file
		fclose( poXferInfo->m_hFile );
		poXferInfo->m_hFile  = NULL;
		log_msg( 0, "VxPktHandler: ERROR: %d reading send file at offset %ld when file len %ld file name %s",
					rc,
					poXferInfo->m_u64FileOffs,
					poXferInfo->m_u64FileLen,
					(const char *)poXferInfo->m_strLocalFileName.c_str() );
		return rc;
		/*
		SendError(  poSkt,
					PKT_TYPE_FILE_CHUNK_ACK,
					CMD_TYPE_FILE_SEND, 
					rc, 
					"VxPktHandler: ERROR: %d reading send file at offset %ld when file len %ld file name %s",
					rc,
					poXferInfo->m_u64FileOffs,
					poXferInfo->m_u64FileLen,
					(const char *)poXferInfo->m_strLocalFileName.c_str() );
					*/
	}
	else
	{

		oPkt.u32ChunkLen = u32ChunkLen;
		poXferInfo->m_u64FileOffs += u32ChunkLen;
	}
	return VxSendPkt( poSkt, &oPkt );
}
//============================================================================
//! receive next part of file.. assumes user data derived from VxSktInfoBase
long VxPktHandler::RxFileChunk( VxSktBase * poSkt, VxBaseSktInfo * poSktInfo, PktFileChunk * poPktHdr  )
{
	long rc = 0;
	vx_assert( poSktInfo );
	VxFileXferInfo * poXferInfo = &poSktInfo->m_oRxFileInfo;
	// we are recieving a file
	if( poXferInfo->m_hFile )
	{
		PktFileChunk * poPkt = (PktFileChunk *)poPktHdr;
		//write the chunk of data out to the file
		U32 u32BytesWritten = (U32)fwrite(	poPkt->au8FileChunk,
											1,
											poPkt->u32ChunkLen,
											poXferInfo->m_hFile );
		if( u32BytesWritten != poPkt->u32ChunkLen ) 
		{
			// could not write data to file
			rc = VxGetLastError();
			if( 0 == rc )
			{
				// make and error up
				rc = -1;
			}
			log_msg( 0, "VxPktHandler::RxFileChunk: ERROR %d: writing to file %s",
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );

		}
		else
		{
			//successfull write
			PktFileChunkAck oPkt;
			poXferInfo->m_u64FileOffs += poPkt->u32ChunkLen;
			VxSendPkt( poSkt, &oPkt );
		}
	}
	return 0;
}
//============================================================================-
//! Send error message
RCODE VxPktHandler::SendError(	VxSktBase *		poSkt,		// socket number
								int				iPktType,	// type of packet
								unsigned short	u16Cmd,		// packet command
								long			rc,			// error code
								const char *	pMsg, ...)	// error message
{
	// send an error message
	RCODE rcSendErr = 0;
	//build message on stack so no out of memory issue
	char szBuffer[2048];
	va_list argList;
    va_start(argList, pMsg);
	vsnprintf( szBuffer, 2048, pMsg, argList );
    va_end(argList);
	log_msg( 0, szBuffer );
	// now send the message with correct reply depending on type of packet recieved

	switch( iPktType )
	{	
	case PKT_TYPE_FILE_CHUNK:
	{
		PktFileChunkAck gPkt;
		gPkt.u32Error = rc;
		strcpy( gPkt.as8Arg1, szBuffer );
		VxSendPkt( poSkt, &gPkt );
		break;
	}
	case PKT_TYPE_CMD_1ARG_REQ:
	{
		// make a reply packet
		PktCmd1ArgReply gPkt;
		gPkt.u16Cmd = u16Cmd;
		gPkt.u64Error = rc;
		strcpy( gPkt.as8Arg1, szBuffer );
		VxSendPkt( poSkt, &gPkt );
		break;
	}
	case PKT_TYPE_CMD_2ARG_REQ:
	{
		// make a reply packet
		PktCmd2ArgReply gPkt;
		gPkt.u16Cmd = u16Cmd;
		gPkt.u32Error = rc;
		strcpy( gPkt.as8Arg1, szBuffer );
		VxSendPkt( poSkt, &gPkt );
		break;
	}
	default:
	{
		// must never happen
		vx_assert( FALSE );
		PktErrorMsg( "ERROR: unrecognize packet type in Send Error" );
		break;
	}
	}// end of switch
	return rcSendErr;
}


/*
//============================================================================-
//! fill pkt with another chunk of file to send
long VxPktHandler::FillSendFilePkt( VxSktBase * poSkt, VxBaseSktInfo * poSktInfo, PktFileChunkAck * poPkt )
{
	VxBaseSktInfo * poSktInfo = (VxBaseSktInfo *)pvUserData;
	if( NULL == poSktInfo )
	{
		PktErrorMsg( "VxPktHandler::FillSendFilePkt: NULL skt info" );
		SlytronCloseConnection( poSkt );
		return 0;
	}
	// fill the packet with data from the file
	RCODE rc = 0;
	unsigned long u32BytesRead = 0;
	VxFileXferInfo * poXferInfo = &poSktInfo->m_oTxFileInfo;
	vx_assert( poXferInfo->m_hFile );
	vx_assert( poXferInfo->m_u64FileLen );
	if( poXferInfo->m_u64FileOffs >= poXferInfo->m_u64FileLen )
	{
		//we are done sending file
		if( poXferInfo->m_hFile )
		{
			CloseHandle( poXferInfo->m_hFile );
			poXferInfo->m_hFile  = NULL;
		}
		// move file from to be sent to sent
		poSktInfo->m_astrFilesSent.push_back( poSktInfo->m_astrFilesToSend[0] );
		poSktInfo->m_astrFilesToSend.erase( poSktInfo->m_astrFilesToSend.begin() );
		poSktInfo->m_astrFilesToSend.erase( poSktInfo->m_astrFilesToSend.begin() );

		PktFileSendComplete oPkt;
		VxCalculateFileCheckSum( poXferInfo->m_strLocalFileName.c_str(), oPkt.u32CheckSum );
		VxSendPkt( poSkt, &oPkt );
		PktErrorMsg( "Slytron Client:: %s Done Sending file %s", (const char *)poXferInfo->m_strUserName.c_str(), poXferInfo->m_strLocalFileName.c_str() );
		OnFileSent( poSkt, poSktInfo, poXferInfo->m_strLocalFileName.c_str() );
		// send next file if available
		return BeginFileSend( poSkt, poSktInfo );
	}
	return TxNextFileChunk( poSkt, poSktInfo, poPkt );
}
*/

//============================================================================-
//! start the receive file process
RCODE VxPktHandler::BeginFileRecieve(  VxSktBase * poSkt, void * pvUserData, PktCmd1ArgReq * poPkt )
{
	VxBaseSktInfo * poSktInfo = (VxBaseSktInfo *)pvUserData;
	if( NULL == poSktInfo )
	{
		log_msg( 0, "NULL skt info" );
		VxCloseConnection( poSkt );
		return 0;
	}
	RCODE rc = 0;
	U64 u64FileLen;

	VxFileXferInfo * poXferInfo = &poSktInfo->m_oRxFileInfo;
	if( poXferInfo->m_hFile )
	{
		log_msg( 0, "VxPktHandler: ERROR:(File Receive) receive transfer still in progress" );
		SendError(	poSkt,
					poPkt->m_u16PktLen, 
					poPkt->u16Cmd,
					-1,
					"VxPktHandler::BeginFileRecieve: ERROR:(File Send) receive transfer still in progress" );
		return -1;
	}
	// get file information
	poXferInfo->m_strLocalFileName = poPkt->as8Arg1;
	poXferInfo->m_u64FileLen = poPkt->u64Arg3;
	poXferInfo->m_u64FileOffs = 0;

	// get full path to file
	//VxMakeFullPath( poXferInfo->m_strLocalFileName.c_str() );
	VxMakeDirectory( poXferInfo->m_strLocalFileName.c_str() );
	u64FileLen = VxGetFileLen( poXferInfo->m_strLocalFileName.c_str() );
	// how we create the file depends on the options sent
	switch( poPkt->u64Arg2 )
	{
	case CMD_OPTION_RESUME_IF_FILE_EXIST:
	{
		// open file so we can append
		poXferInfo->m_hFile = fopen( poXferInfo->m_strLocalFileName.c_str(), "a+" ); // pointer to name of the file
		if( NULL == poXferInfo->m_hFile )
		{
			// failed to open file
			poXferInfo->m_hFile = NULL;
			rc = VxGetLastError();
			log_msg( 0, "Slytron Client: ERROR:(File Send) %d File %s could not be created", 
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );
			SendError(	poSkt,
						poPkt->m_u16PktLen, 
						poPkt->u16Cmd, 
						rc, 
						"Slytron Client: ERROR:(File Send) %d File %s could not be created", 
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );
			return rc;
		}
		poXferInfo->m_u64FileOffs = u64FileLen;
		// we have valid file so seek to end so we can resume if partial file exists
		if( 0 != poXferInfo->m_u64FileOffs )
		{
			// seek to offset for resume
			if( rc = VxFileSeek(poXferInfo->m_hFile, poXferInfo->m_u64FileOffs ) )
			{
				// seek failed
				fclose( poXferInfo->m_hFile );
				poXferInfo->m_hFile = NULL;
				log_msg( 0, "Slytron Client: ERROR: (File Send) could not seek to position %d in file %s",
							poXferInfo->m_u64FileOffs,
							(const char *)poXferInfo->m_strLocalFileName.c_str() );
				SendError(	poSkt,
							poPkt->m_u16PktLen, 
							poPkt->u16Cmd, 
							rc, 
							"Slytron Client: ERROR: (File Send) could not seek to position %d in file %s",
							poXferInfo->m_u64FileOffs,
							(const char *)poXferInfo->m_strLocalFileName.c_str() );
				return rc;
			}
		}
		break;
	}
	case CMD_OPTION_REPLACE_IF_FILE_EXIST:
	{
		// open file and truncate if exists
		poXferInfo->m_hFile = fopen( poXferInfo->m_strLocalFileName.c_str(), "wb+" ); // pointer to name of the file
		if( NULL == poXferInfo->m_hFile )
		{
			// failed to open file
			poXferInfo->m_hFile = NULL;
			rc = VxGetLastError();
			log_msg( 0, "Slytron Client: ERROR: %d File %s could not be created", 
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );
			SendError(	poSkt,
						poPkt->m_u16PktLen, 
						poPkt->u16Cmd, 
						rc,  
						"Slytron Client: ERROR: %d File %s could not be created", 
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );
			return rc;
		}
		break;
	}
	case CMD_OPTION_FAIL_IF_FILE_EXIST:
	{
		// open file and return error if exists
		poXferInfo->m_hFile = fopen( poXferInfo->m_strLocalFileName.c_str(), "rb" ); // pointer to name of the file
		if( NULL != poXferInfo->m_hFile )
		{
			// failed to open file
			poXferInfo->m_hFile = NULL;
			rc = VxGetLastError();
			log_msg( 0, "Slytron Client: ERROR: %d File %s did not exist", 
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );
			SendError(	poSkt,
						poPkt->m_u16PktLen, 
						poPkt->u16Cmd, 
						rc, 
						"Slytron Client: ERROR: %d File %s did not exist", 
						rc,
						(const char *)poXferInfo->m_strLocalFileName.c_str() );
			return rc;
		}
		break;
	}
	default:
	{
		log_msg( 0, "Slytron Client: ERROR: Invalid File Send option" );
		SendError(	poSkt,
					poPkt->m_u16PktLen, 
					poPkt->u16Cmd, 
					-1, 
					"Slytron Client: ERROR: Invalid File Send option" );
		return -1;
	}
	};// end of switch
	// we should now have the file information we need so send response
	PktErrorMsg( "Slytron Client::(File Send) start recieving file %s from Admin %s", 
					(const char *)poXferInfo->m_strLocalFileName.c_str(),
					(const char *)poXferInfo->m_strUserName.c_str() );
	PktCmd1ArgReply oPkt;
	oPkt.u16Cmd = poPkt->u16Cmd;
	oPkt.u64Arg2 = poXferInfo->m_u64FileOffs;
	oPkt.CalcPktLen();

	// send the reply
	VxSendPkt( poSkt, &oPkt );

	return rc;
}
//============================================================================-
//! finalize receive file and close file
RCODE VxPktHandler::FinishFileRecieve( VxSktBase * poSkt, VxBaseSktInfo *  poSktInfo, PktFileSendComplete * poPkt )
{
	// done recieving file
	if( poSktInfo->m_oRxFileInfo.m_hFile )
	{
		fclose( poSktInfo->m_oRxFileInfo.m_hFile );
		poSktInfo->m_oRxFileInfo.m_hFile = NULL;
	}
	// let other act on the recieved file
	std::string strFileName = poSktInfo->m_oRxFileInfo.m_strLocalFileName;
	poSktInfo->m_astrFilesRecieved.push_back( strFileName );

	//=== acknowlege with checksum of file ===//
	PktFileSendCompleteAck oPkt;
	VxCalculateFileCheckSum( strFileName.c_str(), oPkt.u32CheckSum );
	OnFileRecieved( poSkt, poSktInfo, strFileName.c_str() );
	VxSendPkt( poSkt, &oPkt );
	log_msg( 0,  "VxPktHandler: Done Recieving file %s", strFileName.c_str() );
	return 0;
}
/*
//============================================================================
//! start the file send process
RCODE VxPktHandler::StartFileSend( VxSktBase * poSkt, PktCmd1ArgReq * poPkt )
{
	CliGameSktInfo * poInfo = NULL;
	SktGetUserData( poSkt, (void **)&poInfo);
	if( NULL == poInfo )
	{
		log_msg( 0, "NULL skt info" );
		SlytronCloseConnection( poSkt );
		return 0;
	}
	long s32SeekHigh = 0;
	RCODE rc = 0;

	VxFileXferInfo * poXferInfo = &poInfo->m_oRxFileInfo;
	//first we must determine if there are too many transfers in progress 
	// and if so the dont allow a new transfer to start
	std::vector< std::string> acsAdmins;
	GetAdminsTransferingFiles( &acsAdmins );
	if( (int)acsAdmins.size() >= m_iMaxSimultaneousFileXfers )
	{
		// we are allready at our quota of file transfers
		// tell admin which admins are allready transfering files
		std::string csAdmins;
		std::string csTemp;

		// make list of admins into one string
		for( int i = 0; i < (int)acsAdmins.size(); i++ )
		{
			if( acsAdmins[ i ].size() + csAdmins.size() < ( MAX_ERR_MSG_LEN - 80 ) )
			{
				// it will fitg
				csTemp = acsAdmins[ i ];
				csAdmins += csTemp;
				csAdmins += ",";
			}
		}
		SendError(	poSkt,
					poPkt->m_u16PktLen, 
					poPkt->u16Cmd,
					-1,
					"Slytron Client: ERROR:(File Get) Admin(s) %s is allready transfering files", (const char *) csAdmins.c_str() );

	}

	strcpy( poXferInfo->m_strLocalFileName.c_str(), poPkt->as8Arg1 );
	// make full path to the file
	VxMakeFullPath( poXferInfo->m_strLocalFileName.c_str() );

	poXferInfo->m_u64FileLen = 0;
	poXferInfo->m_u64FileOffs = 0;


	if( poXferInfo->m_hFile )
	{
		SendError(	poSkt,
					PKT_TYPE_CMD_1ARG_REQ, 
					CMD_TYPE_FILE_GET, 
					-1, 
					"Slytron Client: ERROR: File transfer still in progress" );
		return -1;
	}
	unsigned long u64FileLen = VxGetFileLen( (char *)(const char *)poXferInfo->m_strLocalFileName.c_str() ); // file length
	poXferInfo->m_u64FileLen = u64FileLen; // save file len
	if( 0 == u64FileLen )
	{
		// no file found to send
		SendError(	poSkt,
					PKT_TYPE_CMD_1ARG_REQ, 
					CMD_TYPE_FILE_GET, 
					-1, 
					"Slytron Client: ERROR: File %s was not found to send", poXferInfo->m_strLocalFileName.c_str() );
		return -1;
	}

	if( CMD_OPTION_RESUME_IF_FILE_EXIST == poPkt->u64Arg2 ) // arg2 is resume options
	{
		// resume from current length
		poXferInfo->m_u64FileOffs = poPkt->u64Arg3; //arg3 is resume offset
	}
	if( poXferInfo->m_u64FileOffs >= u64FileLen )
	{
		// this is a case where admin user is resuming but the file on his computer is greater or
		// same length as the file on the listen computer
		// should we error ?
		PktCmd1ArgReply gPktReply;
		gPktReply.u16Cmd = poPkt->u16Cmd;
		gPktReply.u64Arg2 = poXferInfo->m_u64FileLen;
		VxSendPkt( poSkt, &gPktReply );
		return 0;
	}
	// open file to be transfered 
	poXferInfo->m_hFile = fopen( poXferInfo->m_strLocalFileName.c_str(), "wb+" ); 
	if( NULL == poXferInfo->m_hFile )
	{
		// open file failed
		poXferInfo->m_hFile = NULL;
		SendError(  poSkt,
					PKT_TYPE_CMD_1ARG_REQ, 
					CMD_TYPE_FILE_GET, 
					-1, 
					"Slytron Client: ERROR: could not open file %s to be sent",
					(const char *)poXferInfo->m_strLocalFileName.c_str() );
		return -1;
	}

	if( 0 != poXferInfo->m_u64FileOffs )
	{
		// seek to offset for resume
		if( rc = VxFileSeek(	poXferInfo->m_hFile, poXferInfo->m_u64FileOffs ) )
		{
			// seek failed
			fclose( poXferInfo->m_hFile );
			poXferInfo->m_hFile = NULL;
			SendError( poSkt,
					PKT_TYPE_CMD_1ARG_REQ, 
					CMD_TYPE_FILE_GET, 
					-1, 
					"Slytron Client: ERROR: (File Get)could not seek to position %d in file to be sent %s",
					poXferInfo->m_u64FileOffs,
					(const char *)poXferInfo->m_strLocalFileName.c_str() );
			return -1;
		}
	}
	// send ack so admin tool get file len
	PktCmd1ArgReply gPktReply;
	gPktReply.u16Cmd = poPkt->u16Cmd;
	gPktReply.u64Arg2 = poXferInfo->m_u64FileLen;
	VxSendPkt( poSkt, &gPktReply );
	PktErrorMsg( "Slytron Client::(File Get) start sending file %s from Admin %s", 
					(const char *)poXferInfo->m_strLocalFileName.c_str(),
					(const char *)poXferInfo->m_strUserName.c_str() );

	// file is open and setup so send first chunk of data
	PktFileChunk gPkt;
	rc = FillSendFilePkt( poSkt, &gPkt );
	if( rc )
	{
		// Fill may have completed the file so do nothing
		return rc;
	}
	// send the chunk of data
	VxSendPkt( poSkt, &gPkt );
	return rc;
}
//============================================================================
//! fill and send another chunk of a file
RCODE VxPktHandler::SendNextFileChunk( VxSktBase * poSkt )
{
	CliGameSktInfo * poInfo = NULL;
	SktGetUserData( poSkt, (void **)&poInfo );
	if( NULL == poInfo )
	{
		log_msg( 0, "NULL skt info" );
		SlytronCloseConnection( poSkt );
		return 0;
	}
	// fill a packet with data from file and send it
	RCODE rc;
	PktFileChunk gPkt;
	VxFileXferInfo * poXferInfo = &poInfo->m_oRxFileInfo;
	rc = FillSendFilePkt( poSkt, &gPkt );
	if( rc )
	{
		// FillSendFilePkt had error or completed the file so do nothing
		return rc;
	}
	// send the chunk of data
	rc = VxSendPkt( poSkt, &gPkt );
	if( rc )
	{
		fclose( poXferInfo->m_hFile );
		poXferInfo->m_hFile = NULL;
		SendError( poSkt,
					PKT_TYPE_FILE_CHUNK_ACK, 
					0, 
					rc, 
					"VxPktHandler ERROR: socket error %d %s sending data at position %d in file to be sent %s",
					rc,
					DescribeSktErr( rc ),
					poXferInfo->m_u64FileOffs,
					(const char *)poXferInfo->m_strLocalFileName.c_str() );
	}
	return rc;
}
*/

